#!/usr/bin/python
"""
Unit tests for the grill.core module
"""
import fix_path

import unittest
from unittest import TestCase

from grill.core import *

class ArgTypesDecoratorTest (TestCase):
	"""
	Test that the arg_types decorator works as defined
	"""
	def testSimpleTypes (self):
		"""Check that arg_types decorator works with simple types"""
		@arg_types(int, basestring)
		def take_int (i, message):
			pass
		
		take_int(1, "hello")
		
		self.assertRaises(TypeError, take_int, "my string")
		self.assertRaises(TypeError, take_int, 1, 2)
		self.assertRaises(TypeError, take_int, 1, None)
		self.assertRaises(TypeError, take_int, None, "my string")
		
	def testNullableArgs (self):
		"""Check that arg_types we can allow nullable args"""
		@arg_types(int, basestring, nullable=True)
		def take_int (i, message):
			pass
		
		take_int(1, "hello")
		take_int (None, "asd")
		take_int (1, None)
		take_int (None, None)
		
		self.assertRaises(TypeError, take_int, "my string", None)
		self.assertRaises(TypeError, take_int, None, 2)
	
	def testAnyType (self):
		@arg_types (AnyType)
		def take_any (i):
			pass
		
		take_any (1)
		take_any ("j")
		take_any ([1,2])
		take_any (int)
		
		self.assertRaises (TypeError, take_any, None)
		
		@arg_types (AnyType, nullable=True)
		def take_any_any (i):
			pass
		
		take_any_any (None)
		
	def testCallableType (self):
		@arg_types (CallableType)
		def take_callable (i):
			pass
		
		class Foo :
			def bar (self, baz):
				pass
		
		take_callable (int)
		take_callable (take_callable)
		take_callable (Foo)
		take_callable (Foo.bar)
		
		self.assertRaises (TypeError, take_callable, 1)
		self.assertRaises (TypeError, take_callable, "h")
		
class UserExceptionTest (TestCase):
	"""
	Tests for grill.core.UserException
	"""
	def testBadInput (self):
		"""Constructor should throw a TypeError on non-string input"""
		self.assertRaises(TypeError, UserError, 1)
		self.assertRaises(TypeError, UserError, None)

		# Check normal instantiation works
		UserError ("This is a test UserException")
		

class ToolArgumentSpecTest (TestCase):
	"""
	Tests for grill.core.ToolArgumentSpec
	"""
	def testBadInput (self):
		"""
		Check that we behave correctly on bad input to constructor.
		This should be all be guarded by the arg_types decorator,
		but what the heck...
		"""
		self.assertRaises (TypeError, ToolArgumentSpec, 
						   "test", "ERROR", ToolArgumentSpec.REQUIRED, 
						   "Description")
		self.assertRaises (TypeError, ToolArgumentSpec, 
						   None, int, ToolArgumentSpec.REQUIRED, 
						   "Description")
		self.assertRaises (TypeError, ToolArgumentSpec, 
						   "test", int, -1, 
						   "Description")
		self.assertRaises (TypeError, ToolArgumentSpec, 
						   "test", int, ToolArgumentSpec.REQUIRED, 
						   None)
		
		# Check that correct instantiation works
		ToolArgumentSpec ("test", int, ToolArgumentSpec.REQUIRED, 
						  "Description of test tool")
	
	def testConsistency (self):
		"""
		Test that the arguments passed to the constructor are returned
		identically.
		"""
		spec = ToolArgumentSpec ("test", int, ToolArgumentSpec.REQUIRED, 
								 "Description of test tool")
		
		self.assertEquals ("test", spec.get_name ())
		self.assertEquals (int, spec.get_checker ())
		self.assertEquals ("Description of test tool", spec.get_description ())
	
class ToolArgumentTest (TestCase):
	"""
	Tests for grill.core.ToolArgument
	"""
	def setUp (self):
		self.spec = ToolArgumentSpec ("test", checkInteger, 
									  ToolArgumentSpec.REQUIRED, 
									  "Description of test argument")

	def tearDown (self):
		pass

	def testCreate (self):
		"""
		Test that we cannot create illegal args and that we can indeed create
		legal ones.
		"""
		self.assertRaises (InvalidArgumentError, ToolArgument, self.spec, "a")
		self.assertRaises (InvalidArgumentError, ToolArgument, self.spec, "True")
		self.assertRaises (InvalidArgumentError, ToolArgument, self.spec, "1.1")
		
		# check that we can create an argument
		arg = ToolArgument (self.spec, 10)
		ToolArgument (self.spec, "1")

class ToolSpecTest (TestCase):
	"""
	Tests for grill.core.ToolSpec
	"""
	def setUp (self):
		arg_specs = []
		arg_specs.append (ToolArgumentSpec ("testint", checkInteger, 
											ToolArgumentSpec.REQUIRED, 
											"Integer argument"))
		arg_specs.append (ToolArgumentSpec ("testbool", checkBoolean, 
											ToolArgumentSpec.OPTIONAL, 
											"Boolean argument"))
		
		self.arg_specs = arg_specs
	
	def testCreate (self):
		spec = ToolSpec ("testtool", self.arg_specs)
		
	def testDuplicateArgSpec (self):
		self.arg_specs.append (ToolArgumentSpec ("testbool", checkBoolean, 
												ToolArgumentSpec.OPTIONAL, 
												"Boolean argument"))
												
		self.assertRaises (MalformedToolSpec, ToolSpec, 
						   "testtool", self.arg_specs)

class ToolTest (TestCase):
	"""
	Tests for grill.core.Tool
	"""
	def setUp (self):
		arg_specs = []
		arg_specs.append (ToolArgumentSpec ("testint", checkInteger, 
											ToolArgumentSpec.REQUIRED, 
											"Integer argument"))
		arg_specs.append (ToolArgumentSpec ("testbool", checkBoolean, 
											ToolArgumentSpec.OPTIONAL, 
											"Boolean argument"))
		
		if not Tool.exists("testtool"):
			self.tool_spec = ToolSpec ("testtool", arg_specs)
	
	def testGetInstance (self):
		tool = Tool.get ("testtool")
		self.failIf (not isinstance(tool, Tool))
		
# Run test suite from CLI						  
if __name__ == "__main__":
	unittest.main()
